package br.com.comanda.dao.autosql;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import android.database.Cursor;
import android.util.Log;
import br.com.comanda.dao.autosql.annotations.Column;
import br.com.comanda.dao.autosql.dto.DTO;
import br.com.comanda.dao.autosql.dto.Dominio;
import br.com.comanda.dao.autosql.exception.AutoSQLException;
import br.com.comanda.dao.autosql.exception.TypeNotSupportedException;
import br.com.comanda.dao.autosql.metadadoscache.MetaDadosCache;
import br.com.comanda.dao.autosql.metadadoscache.MetaDadosExtractor;
import br.com.comanda.util.Constantes;
import br.com.comanda.util.DateUtils;

/**
 * Classe para realizar rowMapper: Preenche um objeto automaticamente a partir
 * de um cursor SQL.
 * 
 * @author thiago
 * @since 09/01/2013
 */
public final class RowMapper {

	private RowMapper() {
		super();
	}

	/**
	 * Realiza o fetch do cursor preenchendo uma lista de objetos
	 */
	public static List<?> fetchCursor(Class<?> clazz, Cursor cursor,
			boolean simpleSearch) throws AutoSQLException {
		List<Object> list = new ArrayList<Object>();

		try {
			List<MetaDadosCache> cacheList = MetaDadosExtractor.extract(clazz);
			cursor.moveToFirst();

			while (!cursor.isAfterLast()) {
				Object instance = clazz.newInstance();

				for (MetaDadosCache cache : cacheList) {
					Column coluna = cache.getColumn();
					Dominio dominio = cache.getDominio();

					if (simpleSearch) {
						if (coluna != null && !coluna.simple()) {
							continue;
						}
					}

					if ((coluna != null && coluna.selectable())
							|| (clazz.isAnnotationPresent(DTO.class) && dominio != null)) {

						String columnName = null;

						if (coluna == null) {
							if (dominio.alias().length() > 0) {
								columnName = dominio.alias();
							} else {
								columnName = dominio.coluna();
							}
						} else {
							columnName = coluna.name();
						}

						int index = cursor.getColumnIndex(columnName);
						Object value = getFromCursor(cursor, cache.getField()
								.getType(), index);

						if (Modifier.isPublic(cache.getField().getModifiers())) {
							cache.getField().set(instance, value);
						} else {
							cache.getSet().invoke(instance, value);
						}
					}
				}

				list.add(instance);
				cursor.moveToNext();
			}
		} catch (Exception e) {
			Log.e(Constantes.TAG_LOG, e.getMessage(), e);
			throw new AutoSQLException("Erro ao executar comando SQL", e);
		} finally {
			cursor.close();
		}

		return list;
	}

	/**
	 * Busca um valor no cursor de acordo com o tipo passado
	 */
	public static Object getFromCursor(Cursor cursor, Class<?> type,
			int position) throws TypeNotSupportedException {
		if (type.isPrimitive()) {
			type = Wrapper.box(type);
		}

		if (type == String.class) {
			return cursor.getString(position);
		} else if (type == Long.class) {
			return cursor.getLong(position);
		} else if (type == Double.class) {
			return cursor.getDouble(position);
		} else if (type == Integer.class) {
			return cursor.getInt(position);
		} else if (type == Boolean.class) {
			return cursor.getInt(position) == 1;
		} else if (type == Short.class) {
			return cursor.getShort(position);
		} else if (type == Date.class) {
			return DateUtils.stringDateOrNull(cursor.getString(position));
		} else if (type == Float.class) {
			return cursor.getFloat(position);
		} else if (type == Character.class) {
			String charact = cursor.getString(position);
			return charact != null && charact.equals("") ? ' '
					: (charact == null ? ' ' : charact.charAt(0));
		}
		throw new TypeNotSupportedException("Tipo nao suportado");
	}
}
